fix: stale references and lost Gemfile pins after Pro migration (#3104)#3232
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughReplaces the import-only scan in react_on_rails:doctor with a broader "base package reference" scan that detects CommonJS ChangesBase Package Reference Detection Enhancement
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR extends the Confidence Score: 4/5Safe to merge; the only finding is a P2 wording improvement to the warning message. No logic errors or regressions introduced. The two new regex patterns are correct and the tests validate them. The only concern is a P2: the existing warning message and fix suggestion are framed around import/require patterns and don't give accurate guidance for the newly-detected mock-call and declare module cases. react_on_rails/lib/react_on_rails/doctor.rb — warning message wording should be updated to mention mock/declare-module patterns. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[check_base_package_imports] --> B[resolve_js_source_path]
B --> C[Glob js/jsx/ts/tsx files]
C --> D{File content matches?}
D -->|import from react-on-rails| E[files_with_base_import << file]
D -->|require react-on-rails| E
D -->|jest.mock react-on-rails NEW| E
D -->|declare module react-on-rails NEW| E
D -->|no match| F[next file]
E --> G{Any files collected?}
G -->|yes| H[add_warning with file list]
G -->|no| I[add_success]
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Warning message misleads for mock and declaration matches
- Generalized the doctor warning and spec coverage so mock and declaration matches are described as package references with applicable remediation guidance.
Or push these changes by commenting:
@cursor push 2c7191062e
Preview (2c7191062e)
diff --git a/react_on_rails/lib/react_on_rails/doctor.rb b/react_on_rails/lib/react_on_rails/doctor.rb
--- a/react_on_rails/lib/react_on_rails/doctor.rb
+++ b/react_on_rails/lib/react_on_rails/doctor.rb
@@ -2747,9 +2747,9 @@
end
# The base 'react-on-rails' npm package is a transitive dependency of 'react-on-rails-pro',
- # so `import ... from 'react-on-rails'` resolves silently — loading the base package instead
- # of Pro. Components registered through the base package won't have Pro features (streaming,
- # caching, RSC), and may cause "component not registered" errors at runtime.
+ # so references to 'react-on-rails' resolve silently — loading the base package instead of Pro.
+ # Components registered through the base package won't have Pro features (streaming, caching,
+ # RSC), and may cause "component not registered" errors at runtime.
BASE_PACKAGE_IMPORT_PATTERN = %r{\bfrom\s+['"]react-on-rails(?:/[^'"]*)?['"]}
BASE_PACKAGE_REQUIRE_PATTERN = %r{\brequire\s*\(\s*['"]react-on-rails(?:/[^'"]*)?['"]\s*\)}
BASE_PACKAGE_MOCK_PATTERN = %r{\b\w+\.(?:mock|unmock|doMock|dontMock|requireActual|requireMock)\s*\(\s*['"]react-on-rails(?:/[^'"]*)?['"]} # rubocop:disable Layout/LineLength
@@ -2777,13 +2777,13 @@
checker.add_success("✅ No base 'react-on-rails' imports found (Pro package used correctly)")
else
checker.add_warning(<<~MSG.strip)
- ⚠️ Found imports from 'react-on-rails' instead of 'react-on-rails-pro':
+ ⚠️ Found references to 'react-on-rails' instead of 'react-on-rails-pro':
#{files_with_base_import.map { |f| " • #{f}" }.join("\n")}
- The base package is a transitive dependency of Pro, so these imports resolve
+ The base package is a transitive dependency of Pro, so these references resolve
silently but load the base version without Pro features.
- Fix: Update imports to use 'react-on-rails-pro':
+ Fix: Update imports, require calls, mocks, and declarations to use 'react-on-rails-pro':
import ReactOnRails from 'react-on-rails-pro'; // server
import ReactOnRails from 'react-on-rails-pro/client'; // client
MSG
diff --git a/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb b/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
--- a/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
+++ b/react_on_rails/spec/lib/react_on_rails/doctor_spec.rb
@@ -2546,6 +2546,7 @@
doctor.send(:check_base_package_imports)
warning_msgs = checker.messages.select { |m| m[:type] == :warning }
expect(warning_msgs.any? { |m| m[:content].include?("app.test.ts") }).to be true
+ expect(warning_msgs.any? { |m| m[:content].include?("Found references to 'react-on-rails'") }).to be true
end
end
@@ -2565,6 +2566,7 @@
doctor.send(:check_base_package_imports)
warning_msgs = checker.messages.select { |m| m[:type] == :warning }
expect(warning_msgs.any? { |m| m[:content].include?("react-on-rails.d.ts") }).to be true
+ expect(warning_msgs.any? { |m| m[:content].include?("imports, require calls, mocks, and declarations") }).to be true
end
endYou can send follow-ups to the cloud agent here.
Review: fix: detect stale Pro migration module specifiersOverall this is a clean, well-scoped addition. The two new regex constants are logically correct and the test structure follows the existing pattern. A few things worth addressing before merge: 🔴 Warning message doesn't cover the new detection typesThe existing warning text (unchanged in this PR) was written only for import/require: When the trigger is a Jest mock or a Fix: Replace all base-package references with 'react-on-rails-pro':
import ReactOnRails from 'react-on-rails-pro'; // ESM import (server)
import ReactOnRails from 'react-on-rails-pro/client'; // ESM import (client)
jest.mock('react-on-rails-pro', ...); // test mock
declare module 'react-on-rails-pro' { ... } // TypeScript augmentation🟡 Test gaps
🟢 Things that look good
|
Code ReviewOverall: A focused, low-risk improvement to the Pro migration diagnostic. The two new regex patterns correctly catch categories of base-package references that were previously invisible to the scanner, and the specs cover both new paths. A few things worth addressing before merge: Warning message is stale for the new pattern typesThe existing warning text (
The success path also reads "No base 'react-on-rails' imports found" — technically wrong now that mocks and type declarations are in scope. Suggested language update (in checker.add_success("✅ No base 'react-on-rails' references found (Pro package used correctly)")
# ...
checker.add_warning(<<~MSG.strip)
⚠️ Found references to 'react-on-rails' instead of 'react-on-rails-pro':
#{files_with_base_import.map { |f| " • #{f}" }.join("\n")}
The base package is a transitive dependency of Pro, so these references resolve
silently but load the base version without Pro features.
Fix: replace base-package references with their Pro equivalents:
import ReactOnRails from 'react-on-rails-pro'; // ES import (server)
import ReactOnRails from 'react-on-rails-pro/client'; // ES import (client)
jest.mock('react-on-rails-pro', ...) // Jest/Vitest mock
// delete stale 'declare module' blocks in .d.ts files
MSG
|
Code ReviewSummaryThis PR extends Regex correctness ✅Both new patterns correctly exclude
File extension coverage ✅
Naming inconsistency
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@react_on_rails/lib/react_on_rails/doctor.rb`:
- Around line 2755-2770: The BASE_PACKAGE_MOCK_PATTERN used in
check_base_package_imports is missing Vitest helpers; update the pattern
(BASE_PACKAGE_MOCK_PATTERN) to also match importActual/importMock calls and the
vi.importActual/vi.importMock forms so the doctor flags "Jest/Vitest mock"
usages like importActual('react-on-rails...') and
vi.importMock('react-on-rails...'); modify the regex to include both
importActual/importMock and optional vi. prefix variants while keeping existing
Jest variants (mock/unmock/doMock/dontMock/requireActual/requireMock).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 90e0860f-8a55-42d6-9b9a-5b52a866d790
📒 Files selected for processing (3)
CHANGELOG.mdreact_on_rails/lib/react_on_rails/doctor.rbreact_on_rails/spec/lib/react_on_rails/doctor_spec.rb
* origin/main: Add /stress-test Claude Code command for adversarial QA (#3207)
Code Review — PR #3232: detect stale Pro migration module specifiersOverall: Clean, well-scoped change. The new patterns close a real gap (jest/vitest mocks and TS A few things worth addressing before merge: 🐛 Potential issue —
|
|
Code Review - PR 3232 Overall: Clean, well-scoped improvement. The refactoring from an imperative each-loop to flat_map+select reads better, and the decomposition into helper methods is a clear win for testability. Test coverage is thorough with positive and negative cases for every new pattern. What the PR does Extends Specific notes
No duplicates from
Minor nit: Summary Implementation is correct and tests are solid. Two actionable items before merge: (1) freeze |
Address-review summaryScan scope: default cutoff (previous summary at 2026-05-16T16:02:45Z). Covers the claude-review pass on the postfix-guard fix (60ca5c3). Nothing actionable. Mattered
Skipped
General overview comment 4467840026 is a non-actionable aggregate. Note: the only red check is No follow-up issue. Next default scan starts after this comment. Say |
Code Review: PR #3232 — Fix stale references and lost Gemfile pins after Pro migrationOverviewThis PR does three things:
The approach is well-thought-out: the generator uses a conservative allowlist for destructive rewrites while the doctor uses broader detection (intentionally including comments, as documented in the warning message). CorrectnessGemfile parsing (ProMigration module): The multi-phase parser for both parenthesized and non-parenthesized gem declarations is correct and well-tested. Handling of Version pin detection ( Regex false-positive check: The doctor's Template literal handling ( Issues Found1. Silent removal of duplicate
Suggestion: Add a warning when a second base gem entry is encountered after 2. Misleading rescue message in The rescue block says 3. Generator reads JS files without encoding validation (pro_generator.rb:274) content = File.read(file)The doctor's equivalent uses 4. Inconsistent
5.
path: "../path#with-hash",would incorrectly treat Doctor: Intentional Design Choices Worth Acknowledging
Test CoverageThe test coverage is very comprehensive (2,003 lines for the generator spec, 4,215 for the doctor spec). Specific scenarios tested include:
Missing test: duplicate SummaryThis is solid, well-tested work that fixes a real user pain point. The Gemfile parsing logic is particularly careful. The main issues are: (1) silent line-dropping on duplicate gem declarations (minor correctness gap), (2) the encoding handling mismatch between generator and doctor, and (3) the fragile |
Code Review — PR #3232OverviewThis PR resolves issue #3104 by (1) extending the Pro migration generator to rewrite Jest/Vitest mock helpers and TypeScript The extraction of shared constants and Gemfile-parsing logic into a new Bugs1. Extra blank line introduced in Gemfile for multiline parenthesised declarations
"#{prefix}#{rest}"When the closing Reproduction: any case with None of the multi-line parenthesised tests use # current (inserts double \n)
"#{prefix}#{rest}"
# fix
"#{prefix.chomp}#{rest}"Add at least one 2. Offset from original match used against rewritten stringIn closing_quote_index = line.index(pending_match[:quote], pending_match.end(0))
The search still finds the correct character because neither package name contains the quote character — a forward search from an earlier offset lands on the right quote. But this relies on an implicit constraint that is easy to break. A comment documenting the assumption, or recording the offset delta after the Design / Behaviour Concerns3. When
|
When a react_on_rails_pro entry was already present, the swap left the now-stale base react_on_rails declaration in the Gemfile and skipped bundle install -- re-introducing the exact stale-reference state issue #3104 set out to eliminate, and diverging from the pre-refactor behavior. The swap now drops the base declaration, runs bundle install, and reports the removal. Also chomp the trailing newline when stripping a parenthesized gem call's closing parenthesis so a multiline gem( ... ) declaration no longer leaves a blank line behind its replacement. Specs updated to assert the removed/bundled behavior and a byte-exact no-blank-line result; the duplicate-declaration spec now uses a Bundler-valid Gemfile so it no longer defends behavior with invalid input. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — PR #3232 (fix: stale references and lost Gemfile pins after Pro migration)OverviewThis PR fixes two genuine user-facing bugs (stale JS references after Pro migration and overwritten Gemfile version pins) by: extracting shared logic into a new The approach is sound and well-targeted. Test coverage is exceptional — particularly the parametric multi-helper table test, the use of CorrectnessGemfile version-pin preservation (
|
Address-review summary —
|
Address-review summaryScan scope: default cutoff was the prior checkpoint at Mattered
Skipped
All review threads on this PR are now resolved (0 unresolved). Next default scan starts after this comment; say |
…3334) ## Summary Follow-up to [PR #3232](#3232). The Pro migration base-package reference scan now covers TypeScript 4.7 `.mts` / `.cts` source files (and their `.d.mts` / `.d.cts` declaration counterparts), matching the existing `.mjs` / `.cjs` coverage. - Adds `mts` and `cts` to `ReactOnRails::ProMigration::JS_SOURCE_EXTENSIONS`. This shared constant drives both `react_on_rails:doctor`'s stale-reference scanner and the Pro generator's import rewriter, so the doctor warns on and the generator can rewrite the new extensions in one step. - `**/*.mts` / `**/*.cts` globs naturally cover `*.d.mts` / `*.d.cts` declaration files, same as `.ts` covers `.d.ts`. - Existing `react-on-rails-pro` false-positive guards (the `(?:/[^'"]*)?['"]` lookahead in every reference pattern) carry over unchanged. Fixes #3250. ## Test plan - [x] `bundle exec rspec spec/lib/react_on_rails/doctor_spec.rb -e "check_base_package_references"` — 34 examples, 0 failures (includes 2 new specs) - [x] `bundle exec rspec spec/react_on_rails/generators/pro_generator_spec.rb` — 122 examples, 0 failures - [x] `bundle exec rubocop lib/react_on_rails/pro_migration.rb spec/lib/react_on_rails/doctor_spec.rb` — no offenses - [x] New positive spec: stale `react-on-rails` references in `.mts`, `.cts`, `.d.mts`, and `.d.cts` files all surface as warnings - [x] New negative spec: `.mts`/`.cts`/`.d.mts` files referencing `react-on-rails-pro` are not flagged <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: expands file-extension globbing for Pro migration/doctor scans and adds regression tests; no runtime rendering or security-sensitive logic changes. > > **Overview** > Extends the Pro migration/`react_on_rails:doctor` base-package reference scan to include TypeScript 4.7 module extensions `*.mts`/`*.cts` (including `*.d.mts`/`*.d.cts` via globbing), matching existing `*.mjs`/`*.cjs` coverage. > > Adds RSpec coverage to ensure `.mts`/`.cts` files with stale `react-on-rails` references are flagged and that correct `react-on-rails-pro` references are not, and documents the fix in `CHANGELOG.md`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a1bc6f9. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Development tools now scan additional TypeScript module formats (.mts/.cts) and their declarations. * **Documentation** * Changelog updated to reflect expanded TypeScript module coverage. * **Tests** * Test coverage extended to validate scanning and warnings for the new TypeScript module formats. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/shakacode/react_on_rails/pull/3334?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spike for #3313. Standalone Prism prototype + 32-case behavior matrix + benchmark + decision record. Lives under react_on_rails/spike/ so it does not touch the production scanner from #3232 (per the issue's non-goals). Recommendation: keep the current text scanner. Revisit when the Ruby floor rises to 3.3 and Prism becomes a free runtime dependency. The empty-else case the issue calls out is solvable cleanly by the Prism prototype (conditional collapse), but the cost of adding prism as a runtime gem for Ruby 3.0-3.2 users outweighs the cleanup for a generator that runs once per migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
jest.mock/vi.mock/requireActual/importActual/ etc. helpers and TypeScriptdeclare module 'react-on-rails'augmentations duringrails generate react_on_rails:pro, alongside the existingimport/require/ dynamic-import handling. Test files and type stubs that reference the base package no longer get left behind pointing at'react-on-rails'after the migration.path:,git:,require:) when swappingreact_on_railsforreact_on_rails_pro. Previously the swap overwrote the user's pin with whichever version the running generator gem happened to be.react_on_rails:doctorto flag the same patterns the rewriter handles, plus side-effect imports (import 'react-on-rails';). The doctor's coverage is a superset of the rewriter's, so any base-package references that the rewriter can't reach (hand-written code added post-migration, third-party type stubs, codebases migrated manually) still surface as warnings.Closes #3104.
Test Plan
bundle exec rspec react_on_rails/spec/lib/react_on_rails/doctor_spec.rb react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rbbundle exec rubocop react_on_rails/lib/react_on_rails/doctor.rb react_on_rails/lib/generators/react_on_rails/pro_generator.rb react_on_rails/lib/generators/react_on_rails/pro_setup.rb react_on_rails/spec/lib/react_on_rails/doctor_spec.rb react_on_rails/spec/react_on_rails/generators/pro_generator_spec.rbgem "react_on_rails", "~> 16.0", plantjest.mock('react-on-rails'),declare module 'react-on-rails', andimport 'react-on-rails';references, runbundle exec rails generate react_on_rails:pro, confirm all references are rewritten and the version pin is preserved.